package interview.readerwriter;

public class ReaderWriter {
	private int reader_count = 0, writer_count = 0, reader_waiting = 0,
			writer_waiting = 0;

	private ReaderWriter rwlock = new ReaderWriter();

	private void writer_enter() {
		while (reader_count > 0 || reader_waiting > 0 || writer_count > 0 || writer_waiting > 0) {
			writer_waiting++;
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			writer_waiting--;
		}
		writer_count++;
	}
	
	private synchronized void writer_exit() {
		writer_count--;
		if (reader_waiting > 0) {
			notifyAll();
		} else if (writer_waiting > 0) {
			notify();
		}
	}

	private void reader_enter() {
		if (writer_count > 0 || writer_waiting > 0) {
			reader_waiting++;
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			reader_waiting--;
		}
		reader_count++;
	}

	private void reader_exit() {
		reader_count--;
		if (reader_count == 0 && writer_waiting > 0) {
			notify();
		}
	}

	public void simulate() {
		int rid = 0, wid = 0;
		Reader r1 = new Reader(rid++);
		r1.start();
		Reader r2 = new Reader(rid++);
		r2.start();

		Writer w1 = new Writer(wid++);
		w1.start();

		Writer w2 = new Writer(wid++);
		w2.start();

		Reader r3 = new Reader(rid++);
		r3.start();

		Writer w3 = new Writer(wid++);
		w3.start();

		try {
			r1.join();
			r2.join();
			w1.join();
			w2.join();
			r3.join();
			w3.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		System.out.println("All done");
	}

	class Writer extends Thread {
		int id;

		public Writer(int wid) {
			this.id = wid;
		}

		@Override
		public void run() {
			rwlock.writer_enter();
			System.out.println("Writer " + id + " is writing...");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("Writer " + id + " done writing...");
			rwlock.writer_exit();
		}

	}

	class Reader extends Thread {
		int id;

		public Reader(int rid) {
			this.id = rid;
		}

		@Override
		public void run() {
			reader_enter();
			System.out.println("Reader " + id + " is reading...");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("Reader " + id + " done reading...");
			reader_exit();
		}
	}
}
